<?php

declare(strict_types=1);

namespace app\common\service;

use app\common\model\ChatMessage;
use app\common\service\user\UserService;
use ramsey\uuid;
use think\facade\Cache;
use think\facade\Log;
use think\facade\Queue;
use Workerman\Connection\TcpConnection;


class ManagerService
{
    // 缓存前缀
    private const CACHE_USER_PREFIX = 'online_users:';
    private const CACHE_ADMIN_PREFIX = 'online_admins:';
    const FIXED_CS_ID = "CS_MASTER";

    const CS_BACKLOG_KEY = 'cs_backlog';

    // 离线消息
    const OFFLINE_MESSAGE_PREFIX = 'offline_message:';

    // 用于保存用户ID与TcpConnection的映射关系
    public static array $userConnections = [];
    const CACHE_USER = 'user:connections:';
    const CACHE_ADMIN = 'admin:connections:';
    public static array $adminConnections = [];


    /**
     * 获取用户连接实例
     *
     * @param $role
     * @param $form
     * @return mixed
     */
    public static function getConnection($role, $form): mixed
    {
        if($role === 'admin') {
            return self::$adminConnections[$form];
        }
        return self::$userConnections[$form];
    }

    /**
     * 绑定用户连接
     *
     * @param TcpConnection $connection
     * @param $userId
     * @param $role
     * @return void
     */
//    public static function bindUser(TcpConnection $connection, $userId, $role): void
//    {
//        $redis = Cache::store('redis')->handler();
//        self::removeConnection($connection);
//
//        $connectionId = spl_object_hash($connection); // 唯一标识连接
//        $connection->uid = $userId;
//        $connection->role = $role;
//
//        // 构造连接元数据
//        $connectionInfo = [
//            'uid' => $userId,
//            'connection_id' => $connectionId,
//            'last_active' => time(),
//            'ip' => $connection->getRemoteIp(),
//            'client_id' => $connection->id,
//            'worker_id' => $connection->worker->id,
//            'pid' => getmypid() // 当前进程ID
//        ];
//
//        if ($role === 'admin') {
//            // 使用哈希存储管理员信息
//            self::$adminConnections[$userId] = $connection;
//            Cache::set(self::CACHE_ADMIN_PREFIX . $userId, [
//                $userId => json_encode($connectionInfo)
//            ]);
//            self::processCsBacklog($connection);
//        } else {
//            // 普通用户存储
//            self::$userConnections[$userId] = $connection;
//            Cache::set(self::CACHE_USER_PREFIX.$userId, json_encode($connectionInfo));
//            self::checkAndSendOfflineMessages($connection, $userId);
//        }
//    }

    public static function bindUser(TcpConnection $connection, $userId, $role): void
    {
        $redis = Cache::store('redis')->handler();

        // 清理旧连接
        self::removeConnection($connection);

        // 生成连接唯一标识
        $connectionId = spl_object_hash($connection);
        $connection->uid = $userId;
        $connection->role = $role;

        // 构造连接元数据
        $connectionInfo = [
            'uid' => $userId,
            'connection_id' => $connectionId,
            'last_active' => time(),
            'ip' => $connection->getRemoteIp(),
            'client_id' => $connection->id,
            'worker_id' => $connection->worker->id,
            'pid' => getmypid(), // 当前进程ID
        ];

        try {
            // 加锁防止并发问题
            $lockKey = "lock:bind_user:" . $userId;
            if (!$redis->set($lockKey, 1, ['NX', 'EX' => 5])) {
                throw new \RuntimeException("用户绑定正在处理中，请稍后再试");
            }

            if ($role === 'admin') {
                // 管理员绑定
                self::$adminConnections[$userId] = $connection;
                $redis->hMSet(self::CACHE_ADMIN_PREFIX . $userId, $connectionInfo);
                self::processCsBacklog($connection);
            } else {
                // 普通用户绑定
                self::$userConnections[$userId] = $connection;
                $redis->hMSet(self::CACHE_USER_PREFIX . $userId, $connectionInfo);
                self::checkAndSendOfflineMessages($connection, $userId);
            }

            // 记录日志
            Log::info("用户 {$userId} 已成功绑定连接 {$connectionId}");

        } catch (\Exception $e) {
            Log::error("用户绑定失败: " . $e->getMessage());
            throw new \RuntimeException("无法绑定用户连接");

        } finally {
            // 释放锁
            $redis->del($lockKey);
        }
    }

    /**
     * 处理客服积压消息
     *
     * @param TcpConnection $csConn
     * @return void
     */
    private static function processCsBacklog(TcpConnection $csConn): void
    {
        $backlog = Cache::get(self::CS_BACKLOG_KEY, []);
        foreach ($backlog as $msg) {
            $csConn->send(json_encode($msg));
        }
        Cache::delete(self::CS_BACKLOG_KEY);
    }

    /**
     * 保存客服积压消息
     *
     * @param array $message
     * @return void
     */
    private static function saveCsBacklog(array $message): void
    {
        $backlog = Cache::get(self::CS_BACKLOG_KEY, []);
        $backlog[] = $message;
        Cache::set(self::CS_BACKLOG_KEY, $backlog);
    }

    /**
     * 用户咨询客服消息
     *
     * @param TcpConnection $userConn
     * @param array $msg
     * @return void
     */
    private static function handleCsMessage(TcpConnection $userConn, array $msg): void
    {
        $csMessage = [
            'code' => 200,
            'method' => 'CS_MSG', // 发送给客服
            'data' => [
                'session_id' => $userConn->uid,
                'content' => $msg['content'],
                'timestamp' => time() * 1000
            ]
        ];

        // 广播给所有客服
        $sent = false;
        foreach (self::$adminConnections as $csConn) {
            $csConn->send(json_encode($csMessage));
            $sent = true;
        }

        // 离线处理
        if (!$sent) {
            self::saveCsBacklog($csMessage);
        }

        // 用户回执
        $userConn->send(json_encode([
            'code' => 200,
            'msg'  => '用户回执',
            'method' => 'MSG_RECEIPT',
            'data' => ['status' => $sent ? 'sent' : 'queued']
        ]));
    }

    /**
     * 处理客服回复用户
     *
     * @param TcpConnection $csConn
     * @param array $data
     * @return bool|null
     */
    public static function handleCsReply(TcpConnection $csConn, array $data): ?bool
    {
        if (empty($data['userId'])) {
            return $csConn->send(json_encode(['code' => 400, 'msg' => '用户ID不存在']));
        }

        // 过滤xss/敏感词内容
        $content = htmlspecialchars($data['content'], ENT_QUOTES);
        $content = filter_sensitive_words($content);

        // 构造用户消息
        $userMsg = [
            'code' => 200,
            'msg'  => '客服回复消息',
            'method' => 'CS_REPLY', // 发送给用户
            'data' => [
                'cs_id' => self::FIXED_CS_ID,
                'content' => $content,
                'timestamp' => time() * 1000
            ]
        ];

        $insert = [
            'message_id' => new uuid(),
            'session_id' => 'user'.$data['userId'].'_'.self::FIXED_CS_ID,
            'sender_id'  => $csConn->uid,
            'receiver_id' => $data['userId'],
            'content'    => $content,
            'status'     => 1,
            'content_type' => $data['action'] ?? '',
        ];

        ChatMessage::create($insert);

        if(isset(self::$userConnections[$data['userId']])) {
            // 用户在线发送给用户
            self::$userConnections[$data['userId']]->send(json_encode($userMsg));
        } else {
            // 离线保存为用户的离线消息
            self::saveOfflineMessage($data['userId'], $userMsg);
        }


        // 客服回执
        return $csConn->send(json_encode([
            'code' => 200,
            'msg'  => '客服回执',
            'method' => 'MSG_RECEIPT',
            'data' => [
                'status' => 'delivered',
                'content' => $insert,
            ]
        ]));
    }

    /**
     * 用户发送聊天信息
     *
     * @param TcpConnection $connection
     * @param $data
     * @return void
     */
    public static function handleChatMessage(TcpConnection $connection, $data): void
    {
        if(!isset($connection->uid) && empty($data['target_id'])) {
            $connection->send(json_encode([
                'code' => 400,
                '参数错误'
            ]));
            return;
        }

        // 过滤xss/敏感词内容
        if($data['action'] === 'text') {
            $content = html_purify($data['content']);
            $content = filter_sensitive_words($content);
        }

        $message = [
            'code' => 200,
            'msg' => '消息接收成功',
            'params' => [
                'action' => 'text',
                'sender_id' => $connection->uid,
                'target_id' => $data['target_id'],
                'extra_params' => $data['extra_params'], // 额外参数
                'content' => $content,
                'message_id' => uniqid(),
                'sender_time' => time(),
            ]
        ];

        // 根据 action 添加或修改特定字段
        if (in_array($data['action'], ['image', 'audio'])) {
            $message['params']['action'] = $data['action'];
            $message['params']['files_path'] = $data['content'] ?? '';
        }
        // 创建发送给接收者的消息
        $receiverMessage = $message;
        $receiverMessage['method'] = 'CHAT_RECEIVE';

        // 创建发送给发送者（即当前连接）的响应消息
        $senderMessage = $message;
        $senderMessage['method'] = 'CHAT_SEND';

        // 发送消息给目标用户
        self::sendMessageToUser($data['target_id'], $receiverMessage, $connection->worker->id);
        // 发送响应给发送者
        $connection->send(json_encode($senderMessage));

        // 队列更新会话表
        Queue::push(\app\common\job\ChatSession::class, [
            'target_user_id' => $data['target_id'],
            'user_id' => $connection->uid,
        ]);
    }

    /**
     * 消息撤回
     *
     * @param TcpConnection $connection
     * @param $data
     * @return void
     */
    public static function recallMsg(TcpConnection $connection, $data): void
    {
        $message = [
            'code' => 200,
            'msg' => '消息撤回成功',
            'params' => [
                'action' => 'recall',
                'sender_id' => $connection->uid,
                'target_id' => $data['target_id'],
                'extra_params' => $data['extra_params'], // 额外参数
                'message_id' => $data['message_id'],
                'recall_time' => time(),
            ]
        ];
        // 创建发送给接收者的消息
        $receiverMessage = $message;
        $receiverMessage['method'] = 'CHAT_RECEIVE';

        // 创建发送给发送者（即当前连接）的响应消息
        $senderMessage = $message;
        $senderMessage['method'] = 'CHAT_SEND';
        // 发送消息给目标用户
        self::sendMessageToUser($data['target_id'], $receiverMessage, $connection->worker->id);
        // 发送响应给发送者
        $connection->send(json_encode($senderMessage));
    }

    /**
     * 移除连接
     * @param TcpConnection $conn
     */
    public static function removeConnection(TcpConnection $conn): void
    {
        if (isset($conn->uid, $conn->role)) {
            if ($conn->role === 'admin') {
                unset(self::$adminConnections[$conn->uid]);
                Cache::delete(self::CACHE_ADMIN_PREFIX . $conn->uid);
            } else {
                unset(self::$userConnections[$conn->uid]);
                Cache::delete(self::CACHE_USER_PREFIX.$conn->uid);
            }
        }
    }

    /**
     * 发送消息给指定用户
     *
     * @param string $userId
     * @param array $message
     * @param int $workerId
     * @return void
     */
    public static function sendMessageToUser(string $userId, array $message, int $workerId): void
    {
        $redis = Cache::store('redis')->handler();

        // 查询目标用户进程
        $targetPid = $redis->hGet('user_process_map', $userId);
        $currentPid = $workerId;

        if ($redis->hGet(self::CACHE_USER_PREFIX.$userId, 'uid')) {
            echo "在线";
            if ($targetPid && $targetPid == $currentPid) {
                // 目标在当前进程，直接发送
                self::$userConnections[$userId]->send(json_encode($message));
                echo "直接发送";
            } else {
                // 跨进程广播
                \Channel\Client::publish('send_to_user', [
                    'to_uid' => $userId,
                    'message' => $message
                ]);
                echo "跨进程广播";
            }
        } else {
            echo "离线";
            // 用户不在线，保存离线消息
            self::saveOfflineMessage($userId, $message);
            Log::error("User {$userId} 离线中.\n");
        }
    }

    /**
     * 保存离线消息
     *
     * @param string $userId
     * @param array $message
     * @return void
     */
    public static function saveOfflineMessage(string $userId, array $message): void
    {
        $offlineMessages = Cache::get(self::OFFLINE_MESSAGE_PREFIX . $userId, []);
        $offlineMessages[] = $message; // 添加新消息到数组
        Cache::set(self::OFFLINE_MESSAGE_PREFIX . $userId, $offlineMessages); // 更新缓存
    }

    /**
     * 当用户上线时调用此方法检查并发送离线消息
     *
     * @param TcpConnection $connection
     * @param string $userId
     * @return void
     */
    public static function checkAndSendOfflineMessages(TcpConnection $connection, string $userId): void
    {
        $offlineMessages = Cache::get(self::OFFLINE_MESSAGE_PREFIX . $userId, []);

        foreach ($offlineMessages as $message) {
            $connection->send(json_encode($message));
        }

        // 清除已发送的离线消息
        Cache::delete(self::OFFLINE_MESSAGE_PREFIX . $userId);
    }

    /**
     * 滑动偏好
     *
     * @param TcpConnection $connection
     * @param $data
     * @return void
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public static function handlePreference(TcpConnection $connection, $data): void
    {
        if(isset($connection->uid) && !empty($data['target_id'])){
            $userId = $connection->uid; // 发送用户ID
            $target_user_id = $data['target_id']; // 接受用户ID
            // 检查用户是否尝试对自己表达偏好
            if ($userId === $data['target_id']) {
                Log::error("$userId: 有危险的用户");
                $connection->send(json_encode([
                    'code' => 400,
                    'msg'  => '不能于自己匹配！'
                ]));
                return;
            }

            $data = [
                'user_id' => $userId, // 发送者的用户ID
                'target_user_id' => $data['target_id'], // 接收者的用户ID
                'preference' => $data['action'], // 喜欢 like 不喜欢 dislike
            ];
            // 实例化
            $userPreference = app()->make(UserPreferenceService::class);
            $info = $userPreference->getInfo($data);
            // 是否已存在喜好表中
            if(!$info) {
                $userPreference->create($data);
            }

            if($data['preference'] === 'like') {
                // 检查对方是否也喜欢当前用户
                $oppositePreference = $userPreference->getInfo([
                    'user_id' => $data['target_user_id'],
                    'target_user_id' => $userId,
                    'preference' => 'like',
                ]);

                if ($oppositePreference) {
                    // 双方都喜欢对方，添加为好友
                    $friendsService = app()->make(FriendsService::class);

                    $send_res = $friendsService->create([
                        'user_id' => $userId,
                        'friend_id' => $data['target_user_id'],
                        'create_time' => time(),
                        'status' => 1,
                    ]);

                    $target_res = $friendsService->create([
                        'user_id' => $data['target_user_id'],
                        'friend_id' => $userId,
                        'create_time' => time(),
                        'status' => 1,
                    ]);

                    // 互相加入为好友成功后
                    if($send_res && $target_res) {
                        $userService = app()->make(UserService::class);
                        // 获取发送者用户信息
                        $send_info = $userService->getUserInfo($userId);
                        // 获取接收者用户信息
                        $target_info = $userService->getUserInfo($target_user_id);

                        // 生成双方会话
                        $common_id = uniqid();
                        $chatSessionService = app()->make(ChatSessionService::class);
                        $chatSessionService->isChatSession($userId, $target_user_id, $common_id);
                        $chatSessionService->isChatSession($target_user_id, $userId, $common_id);

                        // 通知双方
                        $connection->send(json_encode([
                            'code' => 200,
                            'method' => 'FRIEND_SEND',
                            'msg'  => '匹配成功',
                            'params' => [
                                'sender_info' => [
                                    'sender_id' => $userId,
                                    'from_name' => $send_info['nickname'],
                                    'from_avatar' => $send_info['avatar'],
                                ],
                                'target_info' => [
                                    'target_id' => $target_user_id,
                                    'target_name' => $target_info['nickname'],
                                    'target_avatar' => $target_info['avatar']
                                ]
                            ]
                        ]));
                        self::sendMessageToUser($target_user_id, [
                            'code' => 200,
                            'method' => 'FRIEND_RECEIVE',
                            'msg'  => '匹配成功',
                            'params' => [
                                'sender_info' => [
                                    'sender_id' => $userId,
                                    'from_name' => $send_info['nickname'],
                                    'from_avatar' => $send_info['avatar'],
                                ],
                                'target_info' => [
                                    'target_id' => $target_user_id,
                                    'target_name' => $target_info['nickname'],
                                    'target_avatar' => $target_info['avatar']
                                ]
                            ]
                        ], $connection->worker->id);
                    }
                }
            }
        }
    }
}